package com.ebo.storage.redis;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import redis.clients.jedis.BinaryClient.LIST_POSITION;
import redis.clients.util.SafeEncoder;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.SortingParams;
import redis.clients.jedis.Transaction;
import redis.clients.jedis.Tuple;

public class JRedisProxy implements JRedis {

    /******** 使用连接池的初始化方式 *********/
    private ShardingAlgorithm shardingAlgo;
    
    private static PrintStream ps;

    private static boolean record = false;

    public JRedisProxy(ShardingAlgorithm algo) {
        this.shardingAlgo = algo;
        initOperationTimeOutFile();
    }

    private static void initOperationTimeOutFile() {
        try {
            if (record && ps == null) {
                ps = new PrintStream(new FileOutputStream("/usr/local/services/timedOutOperation.dat"));
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
    }

    public String currentPoolUsage() {
        ShardedRedisPool[] allPools = getAllPools();
        StringBuilder sb = new StringBuilder();
        for (ShardedRedisPool pool : allPools) {
            String shardName = pool.getShardName();
            int numActive = pool.getNumActive();
            int numIdle = pool.getNumIdle();
            sb.append(shardName).append("{active=").append(numActive).append(",idle=").append(numIdle).append("}\r\n");
        }
        return sb.toString();
    }

    private ShardedRedisPool[] getAllPools() {
        return shardingAlgo.getAllPools();
    }

    private ShardedRedisPool getPool(byte[] key) {
        return shardingAlgo.shard(key);
    }

    public void disconnect() throws IOException {
        for (ShardedRedisPool pool : getAllPools()) {
            pool.destroy();
        }
    }

    private boolean convertStateReply(String reply) {
        return "OK".equals(reply);
    }

    private boolean convertStateReply(Number reply) {
        return 0 != reply.intValue();
    }



    public static void record() {
        record = true;
        initOperationTimeOutFile();
    }

    public static void disRecord() {
        record = false;
    }

    private void print(String op, String shardName) {
        if (ps != null && record) {
            ps.println(op + "  " + shardName + "  " + new Date());
        }
    }

    @Override
    public boolean del(byte[]... keys) {
        if (keys == null || keys.length == 0)
            throw new IllegalArgumentException("null input when jRedisProxy del keys");
        ShardedRedisPool pool = getPool(keys[0]);
        Jedis jedis = pool.getResource();
        boolean broken = false;
        try {
            return convertStateReply(jedis.del(keys));
        } catch (Exception ex) {
            broken = true;
            print("del", pool.getShardName());
            throw new RuntimeException(ex);
        } finally {
            if (broken) {
                pool.returnBrokenResource(jedis);
            } else {
                pool.returnResource(jedis);
            }
        }
    }

    @Override
    public Long append(byte[] key, byte[] value) {
        ShardedRedisPool pool = getPool(key);
        Jedis jedis = pool.getResource();
        boolean broken = false;
        try {
            return jedis.append(key, value);
        } catch (Exception ex) {
            broken = true;
            print("append", pool.getShardName());
            throw new RuntimeException(ex);
        } finally {
            if (broken) {
                pool.returnBrokenResource(jedis);
            } else {
                pool.returnResource(jedis);
            }
        }
    }

    @Override
    public Long decr(byte[] key) {
        ShardedRedisPool pool = getPool(key);
        Jedis jedis = pool.getResource();
        boolean broken = false;
        try {
            return jedis.decr(key);
        } catch (Exception ex) {
            broken = true;
            print("decr", pool.getShardName());
            throw new RuntimeException(ex);
        } finally {
            if (broken) {
                pool.returnBrokenResource(jedis);
            } else {
                pool.returnResource(jedis);
            }
        }
    }

    @Override
    public Long decrBy(byte[] key, long integer) {
        ShardedRedisPool pool = getPool(key);
        Jedis jedis = pool.getResource();
        boolean broken = false;
        try {
            return jedis.decrBy(key, integer);
        } catch (Exception ex) {
            broken = true;
            print("decrBy", pool.getShardName());
            throw new RuntimeException(ex);
        } finally {
            if (broken) {
                pool.returnBrokenResource(jedis);
            } else {
                pool.returnResource(jedis);
            }
        }
    }

    @Override
    public Boolean exists(byte[] key) {
        ShardedRedisPool pool = getPool(key);
        Jedis jedis = pool.getResource();
        boolean broken = false;
        try {
            return jedis.exists(key);
        } catch (Exception ex) {
            broken = true;
            print("exists", pool.getShardName());
            throw new RuntimeException(ex);
        } finally {
            if (broken) {
                pool.returnBrokenResource(jedis);
            } else {
                pool.returnResource(jedis);
            }
        }
    }

    @Override
    public Long expire(byte[] key, int seconds) {
        ShardedRedisPool pool = getPool(key);
        Jedis jedis = pool.getResource();
        boolean broken = false;
        try {
            return jedis.expire(key, seconds);
        } catch (Exception ex) {
            broken = true;
            print("expire", pool.getShardName());
            throw new RuntimeException(ex);
        } finally {
            if (broken) {
                pool.returnBrokenResource(jedis);
            } else {
                pool.returnResource(jedis);
            }
        }
    }

    @Override
    public Long expireAt(byte[] key, long unixTime) {
        ShardedRedisPool pool = getPool(key);
        Jedis jedis = pool.getResource();
        boolean broken = false;
        try {
            return jedis.expireAt(key, unixTime);
        } catch (Exception ex) {
            broken = true;
            print("expireAt", pool.getShardName());
            throw new RuntimeException(ex);
        } finally {
            if (broken) {
                pool.returnBrokenResource(jedis);
            } else {
                pool.returnResource(jedis);
            }
        }
    }

    @Override
    public byte[] get(byte[] key) {
        ShardedRedisPool pool = getPool(key);
        Jedis jedis = pool.getResource();
        boolean broken = false;
        try {
            return jedis.get(key);
        } catch (Exception ex) {
            broken = true;
            print("get", pool.getShardName());
            throw new RuntimeException(ex);
        } finally {
            if (broken) {
                pool.returnBrokenResource(jedis);
            } else {
                pool.returnResource(jedis);
            }
        }
    }

    @Override
    public List<byte[]> mget(byte[]... keys) {
        int len = keys.length;
        List<byte[]> res = new ArrayList<byte[]>();
        for (int i = 0; i < len; i++) {
            byte[] bytes = this.get(keys[i]);
            if (bytes != null) {
                res.add(bytes);
            }
        }
        return res;
        /*
         * ShardedRedisPool pool = getPool(keys[0]); Jedis jedis =
         * pool.getResource(); try{ return jedis.mget(keys); } catch (Exception
         * ex) { pool.returnBrokenResource(jedis); throw new
         * RuntimeException(ex); } finally { pool.returnResource(jedis); }
         */
    }

    @Override
    public byte[] getSet(byte[] key, byte[] value) {
        ShardedRedisPool pool = getPool(key);
        Jedis jedis = pool.getResource();
        boolean broken = false;
        try {
            return jedis.getSet(key, value);
        } catch (Exception ex) {
            broken = true;
            print("getSet", pool.getShardName());
            throw new RuntimeException(ex);
        } finally {
            if (broken) {
                pool.returnBrokenResource(jedis);
            } else {
                pool.returnResource(jedis);
            }
        }
    }

    @Override
    public boolean hdel(byte[] key, byte[]... fields) {
        ShardedRedisPool pool = getPool(key);
        Jedis jedis = pool.getResource();
        boolean broken = false;
        try {
            return convertStateReply(jedis.hdel(key, fields));
        } catch (Exception ex) {
            broken = true;
            print("hdel", pool.getShardName());
            throw new RuntimeException(ex);
        } finally {
            if (broken) {
                pool.returnBrokenResource(jedis);
            } else {
                pool.returnResource(jedis);
            }
        }
    }
    
    @Override
    public Boolean hexists(byte[] key, byte[] field) {
        ShardedRedisPool pool = getPool(key);
        Jedis jedis = pool.getResource();
        boolean broken = false;
        try {
            return jedis.hexists(key, field);
        } catch (Exception ex) {
            broken = true;
            print("hexists", pool.getShardName());
            throw new RuntimeException(ex);
        } finally {
            if (broken) {
                pool.returnBrokenResource(jedis);
            } else {
                pool.returnResource(jedis);
            }
        }
    }

    @Override
    public byte[] hget(byte[] key, byte[] field) {
        ShardedRedisPool pool = getPool(key);
        Jedis jedis = pool.getResource();
        boolean broken = false;
        try {
            return jedis.hget(key, field);
        } catch (Exception ex) {
            broken = true;
            print("hget", pool.getShardName());
            throw new RuntimeException(ex);
        } finally {
            if (broken) {
                pool.returnBrokenResource(jedis);
            } else {
                pool.returnResource(jedis);
            }
        }
    }

    @Override
    public Map<byte[], byte[]> hgetAll(byte[] key) {
        ShardedRedisPool pool = getPool(key);
        Jedis jedis = pool.getResource();
        boolean broken = false;
        try {
            return jedis.hgetAll(key);
        } catch (Exception ex) {
            broken = true;
            print("hgetAll", pool.getShardName());
            throw new RuntimeException(ex);
        } finally {
            if (broken) {
                pool.returnBrokenResource(jedis);
            } else {
                pool.returnResource(jedis);
            }
        }
    }

    @Override
    public Long hincrBy(byte[] key, byte[] field, long value) {
        ShardedRedisPool pool = getPool(key);
        Jedis jedis = pool.getResource();
        boolean broken = false;
        try {
            return jedis.hincrBy(key, field, value);
        } catch (Exception ex) {
            broken = true;
            print("hincrBy", pool.getShardName());
            throw new RuntimeException(ex);
        } finally {
            if (broken) {
                pool.returnBrokenResource(jedis);
            } else {
                pool.returnResource(jedis);
            }
        }
    }

    @Override
    public Set<byte[]> hkeys(byte[] key) {
        ShardedRedisPool pool = getPool(key);
        Jedis jedis = pool.getResource();
        boolean broken = false;
        try {
            return jedis.hkeys(key);
        } catch (Exception ex) {
            broken = true;
            print("hkeys", pool.getShardName());
            throw new RuntimeException(ex);
        } finally {
            if (broken) {
                pool.returnBrokenResource(jedis);
            } else {
                pool.returnResource(jedis);
            }
        }
    }

    @Override
    public Long hlen(byte[] key) {
        ShardedRedisPool pool = getPool(key);
        Jedis jedis = pool.getResource();
        boolean broken = false;
        try {
            return jedis.hlen(key);
        } catch (Exception ex) {
            broken = true;
            print("hlen", pool.getShardName());
            throw new RuntimeException(ex);
        } finally {
            if (broken) {
                pool.returnBrokenResource(jedis);
            } else {
                pool.returnResource(jedis);
            }
        }
    }

    @Override
    public List<byte[]> hmget(byte[] key, byte[]... fields) {
        ShardedRedisPool pool = getPool(key);
        Jedis jedis = pool.getResource();
        boolean broken = false;
        try {
            return jedis.hmget(key, fields);
        } catch (Exception ex) {
            broken = true;
            print("hmget", pool.getShardName());
            throw new RuntimeException(ex);
        } finally {
            if (broken) {
                pool.returnBrokenResource(jedis);
            } else {
                pool.returnResource(jedis);
            }
        }
    }

    @Override
    public String hmset(byte[] key, Map<byte[], byte[]> hash) {
        ShardedRedisPool pool = getPool(key);
        Jedis jedis = pool.getResource();
        boolean broken = false;
        try {
            return jedis.hmset(key, hash);
        } catch (Exception ex) {
            broken = true;
            print("hmset", pool.getShardName());
            throw new RuntimeException(ex);
        } finally {
            if (broken) {
                pool.returnBrokenResource(jedis);
            } else {
                pool.returnResource(jedis);
            }
        }
    }

    @Override
    public Long hset(byte[] key, byte[] field, byte[] value) {
        ShardedRedisPool pool = getPool(key);
        Jedis jedis = pool.getResource();
        boolean broken = false;
        try {
        	return jedis.hset(key, field, value);
        } catch (Exception ex) {
            broken = true;
            print("hset", pool.getShardName());
            throw new RuntimeException(ex);
        } finally {
            if (broken) {
                pool.returnBrokenResource(jedis);
            } else {
                pool.returnResource(jedis);
            }
        }
    }

    @Override
    public Long hsetnx(byte[] key, byte[] field, byte[] value) {
        ShardedRedisPool pool = getPool(key);
        Jedis jedis = pool.getResource();
        boolean broken = false;
        try {
            return jedis.hsetnx(key, field, value);
        } catch (Exception ex) {
            broken = true;
            print("hsetnx", pool.getShardName());
            throw new RuntimeException(ex);
        } finally {
            if (broken) {
                pool.returnBrokenResource(jedis);
            } else {
                pool.returnResource(jedis);
            }
        }
    }

    @Override
    public Collection<byte[]> hvals(byte[] key) {
        ShardedRedisPool pool = getPool(key);
        Jedis jedis = pool.getResource();
        boolean broken = false;
        try {
            return jedis.hvals(key);
        } catch (Exception ex) {
            broken = true;
            print("hvals", pool.getShardName());
            throw new RuntimeException(ex);
        } finally {
            if (broken) {
                pool.returnBrokenResource(jedis);
            } else {
                pool.returnResource(jedis);
            }
        }
    }

    @Override
    public Long incr(byte[] key) {
        ShardedRedisPool pool = getPool(key);
        Jedis jedis = pool.getResource();
        boolean broken = false;
        try {
            return jedis.incr(key);
        } catch (Exception ex) {
            broken = true;
            print("incr", pool.getShardName());
            throw new RuntimeException(ex);
        } finally {
            if (broken) {
                pool.returnBrokenResource(jedis);
            } else {
                pool.returnResource(jedis);
            }
        }
    }

    @Override
    public Long incrBy(byte[] key, long integer) {
        ShardedRedisPool pool = getPool(key);
        Jedis jedis = pool.getResource();
        boolean broken = false;
        try {
            return jedis.incrBy(key, integer);
        } catch (Exception ex) {
            broken = true;
            print("incrBy", pool.getShardName());
            throw new RuntimeException(ex);
        } finally {
            if (broken) {
                pool.returnBrokenResource(jedis);
            } else {
                pool.returnResource(jedis);
            }
        }
    }

    @Override
    public byte[] lindex(byte[] key, int index) {
        ShardedRedisPool pool = getPool(key);
        Jedis jedis = pool.getResource();
        boolean broken = false;
        try {
            return jedis.lindex(key, index);
        } catch (Exception ex) {
            broken = true;
            print("lindex", pool.getShardName());
            throw new RuntimeException(ex);
        } finally {
            if (broken) {
                pool.returnBrokenResource(jedis);
            } else {
                pool.returnResource(jedis);
            }
        }
    }

    @Override
    public Long linsert(byte[] key, LIST_POSITION where, byte[] pivot, byte[] value) {
        ShardedRedisPool pool = getPool(key);
        Jedis jedis = pool.getResource();
        boolean broken = false;
        try {
            return jedis.linsert(key, where, pivot, value);
        } catch (Exception ex) {
            broken = true;
            print("linsert", pool.getShardName());
            throw new RuntimeException(ex);
        } finally {
            if (broken) {
                pool.returnBrokenResource(jedis);
            } else {
                pool.returnResource(jedis);
            }
        }
    }

    @Override
    public Long llen(byte[] key) {
        ShardedRedisPool pool = getPool(key);
        Jedis jedis = pool.getResource();
        boolean broken = false;
        try {
            return jedis.llen(key);
        } catch (Exception ex) {
            broken = true;
            print("llen", pool.getShardName());
            throw new RuntimeException(ex);
        } finally {
            if (broken) {
                pool.returnBrokenResource(jedis);
            } else {
                pool.returnResource(jedis);
            }
        }
    }

    @Override
    public byte[] lpop(byte[] key) {
        ShardedRedisPool pool = getPool(key);
        Jedis jedis = pool.getResource();
        boolean broken = false;
        try {
            return jedis.lpop(key);
        } catch (Exception ex) {
            broken = true;
            print("lpop", pool.getShardName());
            throw new RuntimeException(ex);
        } finally {
            if (broken) {
                pool.returnBrokenResource(jedis);
            } else {
                pool.returnResource(jedis);
            }
        }
    }

    @Override
    public Long lpush(byte[] key, byte[] string) {
        ShardedRedisPool pool = getPool(key);
        Jedis jedis = pool.getResource();
        boolean broken = false;
        try {
            return jedis.lpush(key, string);
        } catch (Exception ex) {
            broken = true;
            print("lpush", pool.getShardName());
            throw new RuntimeException(ex);
        } finally {
            if (broken) {
                pool.returnBrokenResource(jedis);
            } else {
                pool.returnResource(jedis);
            }
        }
    }

    @Override
    public Set<String> keys(String pattern) {
        ShardedRedisPool pool = getAllPools()[0];
        Jedis jedis = pool.getResource();
        boolean broken = false;
        try {
            return jedis.keys(pattern);
        } catch (Exception ex) {
            broken = true;
            print("keys", pool.getShardName());
            throw new RuntimeException(ex);
        } finally {
            if (broken) {
                pool.returnBrokenResource(jedis);
            } else {
                pool.returnResource(jedis);
            }
        }
    }

    @Override
    public List<byte[]> lrange(byte[] key, int start, int end) {
        ShardedRedisPool pool = getPool(key);
        Jedis jedis = pool.getResource();
        boolean broken = false;
        try {
            return jedis.lrange(key, start, end);
        } catch (Exception ex) {
            broken = true;
            print("lrange", pool.getShardName());
            throw new RuntimeException(ex);
        } finally {
            if (broken) {
                pool.returnBrokenResource(jedis);
            } else {
                pool.returnResource(jedis);
            }
        }
    }

    @Override
    public Long lrem(byte[] key, int count, byte[] value) {
        ShardedRedisPool pool = getPool(key);
        Jedis jedis = pool.getResource();
        boolean broken = false;
        try {
            return jedis.lrem(key, count, value);
        } catch (Exception ex) {
            broken = true;
            print("lrem", pool.getShardName());
            throw new RuntimeException(ex);
        } finally {
            if (broken) {
                pool.returnBrokenResource(jedis);
            } else {
                pool.returnResource(jedis);
            }
        }
    }

    @Override
    public String lset(byte[] key, int index, byte[] value) {
        ShardedRedisPool pool = getPool(key);
        Jedis jedis = pool.getResource();
        boolean broken = false;
        try {
            return jedis.lset(key, index, value);
        } catch (Exception ex) {
            broken = true;
            print("lset", pool.getShardName());
            throw new RuntimeException(ex);
        } finally {
            if (broken) {
                pool.returnBrokenResource(jedis);
            } else {
                pool.returnResource(jedis);
            }
        }
    }

    @Override
    public String ltrim(byte[] key, int start, int end) {
        ShardedRedisPool pool = getPool(key);
        Jedis jedis = pool.getResource();
        boolean broken = false;
        try {
            return jedis.ltrim(key, start, end);
        } catch (Exception ex) {
            broken = true;
            print("ltrim", pool.getShardName());
            throw new RuntimeException(ex);
        } finally {
            if (broken) {
                pool.returnBrokenResource(jedis);
            } else {
                pool.returnResource(jedis);
            }
        }
    }

    @Override
    public byte[] rpop(byte[] key) {
        ShardedRedisPool pool = getPool(key);
        Jedis jedis = pool.getResource();
        boolean broken = false;
        try {
            return jedis.rpop(key);
        } catch (Exception ex) {
            broken = true;
            print("rpop", pool.getShardName());
            throw new RuntimeException(ex);
        } finally {
            if (broken) {
                pool.returnBrokenResource(jedis);
            } else {
                pool.returnResource(jedis);
            }
        }
    }

    @Override
    public Long rpush(byte[] key, byte[] string) {
        ShardedRedisPool pool = getPool(key);
        Jedis jedis = pool.getResource();
        boolean broken = false;
        try {
            return jedis.rpush(key, string);
        } catch (Exception ex) {
            broken = true;
            print("rpush", pool.getShardName());
            throw new RuntimeException(ex);
        } finally {
            if (broken) {
                pool.returnBrokenResource(jedis);
            } else {
                pool.returnResource(jedis);
            }
        }
    }

    @Override
    public Long sadd(byte[] key, byte[] member) {
        ShardedRedisPool pool = getPool(key);
        Jedis jedis = pool.getResource();
        boolean broken = false;
        try {
            return jedis.sadd(key, member);
        } catch (Exception ex) {
            broken = true;
            print("sadd", pool.getShardName());
            throw new RuntimeException(ex);
        } finally {
            if (broken) {
                pool.returnBrokenResource(jedis);
            } else {
                pool.returnResource(jedis);
            }
        }
    }

    @Override
    public Long scard(byte[] key) {
        ShardedRedisPool pool = getPool(key);
        Jedis jedis = pool.getResource();
        boolean broken = false;
        try {
            return jedis.scard(key);
        } catch (Exception ex) {
            broken = true;
            print("scard", pool.getShardName());
            throw new RuntimeException(ex);
        } finally {
            if (broken) {
                pool.returnBrokenResource(jedis);
            } else {
                pool.returnResource(jedis);
            }
        }
    }

    @Override
    public boolean set(byte[] key, byte[] value) {
        ShardedRedisPool pool = getPool(key);
        Jedis jedis = pool.getResource();
        boolean broken = false;
        try {
            return convertStateReply(jedis.set(key, value));
        } catch (Exception ex) {
            broken = true;
            print("set", pool.getShardName());
            throw new RuntimeException(ex);
        } finally {
            if (broken) {
                pool.returnBrokenResource(jedis);
            } else {
                pool.returnResource(jedis);
            }
        }
    }
    
    @Override
    public boolean setnxex(byte[] key, byte[] value, int seconds) {
        ShardedRedisPool pool = getPool(key);
        Jedis jedis = pool.getResource();
        boolean broken = false;
        try {
            return convertStateReply(jedis.set(key, value, SafeEncoder.encode("NX"), SafeEncoder.encode("EX"), seconds));
        } catch (Exception ex) {
            broken = true;
            print("set", pool.getShardName());
            throw new RuntimeException(ex);
        } finally {
            if (broken) {
                pool.returnBrokenResource(jedis);
            } else {
                pool.returnResource(jedis);
            }
        }
    }

    @Override
    public String setex(byte[] key, int seconds, byte[] value) {
        ShardedRedisPool pool = getPool(key);
        Jedis jedis = pool.getResource();
        boolean broken = false;
        try {
            return jedis.setex(key, seconds, value);
        } catch (Exception ex) {
            broken = true;
            print("setex", pool.getShardName());
            throw new RuntimeException(ex);
        } finally {
            if (broken) {
                pool.returnBrokenResource(jedis);
            } else {
                pool.returnResource(jedis);
            }
        }
    }

    @Override
    public Boolean setnx(byte[] key, byte[] value) {
        ShardedRedisPool pool = getPool(key);
        Jedis jedis = pool.getResource();
        boolean broken = false;
        try {
            return jedis.setnx(key, value).intValue() == 1;
        } catch (Exception ex) {
            broken = true;
            print("setnx", pool.getShardName());
            throw new RuntimeException(ex);
        } finally {
            if (broken) {
                pool.returnBrokenResource(jedis);
            } else {
                pool.returnResource(jedis);
            }
        }
    }

    @Override
    public Boolean sismember(byte[] key, byte[] member) {
        ShardedRedisPool pool = getPool(key);
        Jedis jedis = pool.getResource();
        boolean broken = false;
        try {
            return jedis.sismember(key, member);
        } catch (Exception ex) {
            broken = true;
            print("sismember", pool.getShardName());
            throw new RuntimeException(ex);
        } finally {
            if (broken) {
                pool.returnBrokenResource(jedis);
            } else {
                pool.returnResource(jedis);
            }
        }
    }

    @Override
    public Set<byte[]> smembers(byte[] key) {
        ShardedRedisPool pool = getPool(key);
        Jedis jedis = pool.getResource();
        boolean broken = false;
        try {
            return jedis.smembers(key);
        } catch (Exception ex) {
            broken = true;
            print("smembers", pool.getShardName());
            throw new RuntimeException(ex);
        } finally {
            if (broken) {
                pool.returnBrokenResource(jedis);
            } else {
                pool.returnResource(jedis);
            }
        }
    }

    @Override
    public List<byte[]> sort(byte[] key) {
        ShardedRedisPool pool = getPool(key);
        Jedis jedis = pool.getResource();
        boolean broken = false;
        try {
            return jedis.sort(key);
        } catch (Exception ex) {
            broken = true;
            print("sort", pool.getShardName());
            throw new RuntimeException(ex);
        } finally {
            if (broken) {
                pool.returnBrokenResource(jedis);
            } else {
                pool.returnResource(jedis);
            }
        }
    }

    @Override
    public List<byte[]> sort(byte[] key, SortingParams sortingParameters) {
        ShardedRedisPool pool = getPool(key);
        Jedis jedis = pool.getResource();
        boolean broken = false;
        try {
            return jedis.sort(key, sortingParameters);
        } catch (Exception ex) {
            broken = true;
            print("sort", pool.getShardName());
            throw new RuntimeException(ex);
        } finally {
            if (broken) {
                pool.returnBrokenResource(jedis);
            } else {
                pool.returnResource(jedis);
            }
        }
    }

    @Override
    public byte[] spop(byte[] key) {
        ShardedRedisPool pool = getPool(key);
        Jedis jedis = pool.getResource();
        boolean broken = false;
        try {
            return jedis.spop(key);
        } catch (Exception ex) {
            broken = true;
            print("spop", pool.getShardName());
            throw new RuntimeException(ex);
        } finally {
            if (broken) {
                pool.returnBrokenResource(jedis);
            } else {
                pool.returnResource(jedis);
            }
        }
    }

    @Override
    public byte[] srandmember(byte[] key) {
        ShardedRedisPool pool = getPool(key);
        Jedis jedis = pool.getResource();
        boolean broken = false;
        try {
            return jedis.srandmember(key);
        } catch (Exception ex) {
            broken = true;
            print("srandmember", pool.getShardName());
            throw new RuntimeException(ex);
        } finally {
            if (broken) {
                pool.returnBrokenResource(jedis);
            } else {
                pool.returnResource(jedis);
            }
        }
    }

    @Override
    public Long srem(byte[] key, byte[] member) {
        ShardedRedisPool pool = getPool(key);
        Jedis jedis = pool.getResource();
        boolean broken = false;
        try {
            return jedis.srem(key, member);
        } catch (Exception ex) {
            broken = true;
            print("srem", pool.getShardName());
            throw new RuntimeException(ex);
        } finally {
            if (broken) {
                pool.returnBrokenResource(jedis);
            } else {
                pool.returnResource(jedis);
            }
        }
    }
    
    @Override
    public Long smove(byte[] srckey, byte[] dstkey, byte[] member) {
        ShardedRedisPool pool = getPool(srckey);
        Jedis jedis = pool.getResource();
        boolean broken = false;
        try {
            return jedis.smove(srckey, dstkey, member);
        } catch (Exception ex) {
            broken = true;
            print("smove", pool.getShardName());
            throw new RuntimeException(ex);
        } finally {
            if (broken) {
                pool.returnBrokenResource(jedis);
            } else {
                pool.returnResource(jedis);
            }
        }
    }

    @Override
    public byte[] substr(byte[] key, int start, int end) {
        ShardedRedisPool pool = getPool(key);
        Jedis jedis = pool.getResource();
        boolean broken = false;
        try {
            return jedis.substr(key, start, end);
        } catch (Exception ex) {
            broken = true;
            print("substr", pool.getShardName());
            throw new RuntimeException(ex);
        } finally {
            if (broken) {
                pool.returnBrokenResource(jedis);
            } else {
                pool.returnResource(jedis);
            }
        }
    }

    @Override
    public Long ttl(byte[] key) {
        ShardedRedisPool pool = getPool(key);
        Jedis jedis = pool.getResource();
        boolean broken = false;
        try {
            return jedis.ttl(key);
        } catch (Exception ex) {
            broken = true;
            print("ttl", pool.getShardName());
            throw new RuntimeException(ex);
        } finally {
            if (broken) {
                pool.returnBrokenResource(jedis);
            } else {
                pool.returnResource(jedis);
            }
        }
    }

    @Override
    public String type(byte[] key) {
        ShardedRedisPool pool = getPool(key);
        Jedis jedis = pool.getResource();
        boolean broken = false;
        try {
            return jedis.type(key);
        } catch (Exception ex) {
            broken = true;
            print("type", pool.getShardName());
            throw new RuntimeException(ex);
        } finally {
            if (broken) {
                pool.returnBrokenResource(jedis);
            } else {
                pool.returnResource(jedis);
            }
        }
    }

    @Override
    public Long zadd(byte[] key, double score, byte[] member) {
        ShardedRedisPool pool = getPool(key);
        Jedis jedis = pool.getResource();
        boolean broken = false;
        try {
            return jedis.zadd(key, score, member);
        } catch (Exception ex) {
            broken = true;
            print("zadd", pool.getShardName());
            throw new RuntimeException(ex);
        } finally {
            if (broken) {
                pool.returnBrokenResource(jedis);
            } else {
                pool.returnResource(jedis);
            }
        }
    }

    @Override
    public Long zcard(byte[] key) {
        ShardedRedisPool pool = getPool(key);
        Jedis jedis = pool.getResource();
        boolean broken = false;
        try {
            return jedis.zcard(key);
        } catch (Exception ex) {
            broken = true;
            print("zcard", pool.getShardName());
            throw new RuntimeException(ex);
        } finally {
            if (broken) {
                pool.returnBrokenResource(jedis);
            } else {
                pool.returnResource(jedis);
            }
        }
    }

    @Override
    public Long zcount(byte[] key, double min, double max) {
        ShardedRedisPool pool = getPool(key);
        Jedis jedis = pool.getResource();
        boolean broken = false;
        try {
            return jedis.zcount(key, min, max);
        } catch (Exception ex) {
            broken = true;
            print("zcount", pool.getShardName());
            throw new RuntimeException(ex);
        } finally {
            if (broken) {
                pool.returnBrokenResource(jedis);
            } else {
                pool.returnResource(jedis);
            }
        }
    }

    @Override
    public Double zincrby(byte[] key, double score, byte[] member) {
        ShardedRedisPool pool = getPool(key);
        Jedis jedis = pool.getResource();
        boolean broken = false;
        try {
            return jedis.zincrby(key, score, member);
        } catch (Exception ex) {
            broken = true;
            print("zincrby", pool.getShardName());
            throw new RuntimeException(ex);
        } finally {
            if (broken) {
                pool.returnBrokenResource(jedis);
            } else {
                pool.returnResource(jedis);
            }
        }
    }

    @Override
    public Set<byte[]> zrange(byte[] key, int start, int end) {
        ShardedRedisPool pool = getPool(key);
        Jedis jedis = pool.getResource();
        boolean broken = false;
        try {
            return jedis.zrange(key, start, end);
        } catch (Exception ex) {
            broken = true;
            print("zrange", pool.getShardName());
            throw new RuntimeException(ex);
        } finally {
            if (broken) {
                pool.returnBrokenResource(jedis);
            } else {
                pool.returnResource(jedis);
            }
        }
    }

    @Override
    public Set<byte[]> zrangeByScore(byte[] key, double min, double max) {
        ShardedRedisPool pool = getPool(key);
        Jedis jedis = pool.getResource();
        boolean broken = false;
        try {
            return jedis.zrangeByScore(key, min, max);
        } catch (Exception ex) {
            broken = true;
            print("zrangeByScore", pool.getShardName());
            throw new RuntimeException(ex);
        } finally {
            if (broken) {
                pool.returnBrokenResource(jedis);
            } else {
                pool.returnResource(jedis);
            }
        }
    }

    @Override
    public Set<byte[]> zrangeByScore(byte[] key, double min, double max, int offset, int count) {
        ShardedRedisPool pool = getPool(key);
        Jedis jedis = pool.getResource();
        boolean broken = false;
        try {
            return jedis.zrangeByScore(key, min, max, offset, count);
        } catch (Exception ex) {
            broken = true;
            print("zrangeByScore", pool.getShardName());
            throw new RuntimeException(ex);
        } finally {
            if (broken) {
                pool.returnBrokenResource(jedis);
            } else {
                pool.returnResource(jedis);
            }
        }
    }

    @Override
    public Set<Tuple> zrangeByScoreWithScores(byte[] key, double min, double max) {
        ShardedRedisPool pool = getPool(key);
        Jedis jedis = pool.getResource();
        boolean broken = false;
        try {
            return jedis.zrangeByScoreWithScores(key, min, max);
        } catch (Exception ex) {
            broken = true;
            print("zrangeByScoreWithScores", pool.getShardName());
            throw new RuntimeException(ex);
        } finally {
            if (broken) {
                pool.returnBrokenResource(jedis);
            } else {
                pool.returnResource(jedis);
            }
        }
    }

    @Override
    public Set<Tuple> zrangeByScoreWithScores(byte[] key, double min, double max, int offset, int count) {
        ShardedRedisPool pool = getPool(key);
        Jedis jedis = pool.getResource();
        boolean broken = false;
        try {
            return jedis.zrangeByScoreWithScores(key, min, max, offset, count);
        } catch (Exception ex) {
            broken = true;
            print("zrangeByScoreWithScores", pool.getShardName());
            throw new RuntimeException(ex);
        } finally {
            if (broken) {
                pool.returnBrokenResource(jedis);
            } else {
                pool.returnResource(jedis);
            }
        }
    }

    @Override
    public Set<Tuple> zrangeWithScores(byte[] key, int start, int end) {
        ShardedRedisPool pool = getPool(key);
        Jedis jedis = pool.getResource();
        boolean broken = false;
        try {
            return jedis.zrangeWithScores(key, start, end);
        } catch (Exception ex) {
            broken = true;
            print("zrangeWithScores", pool.getShardName());
            throw new RuntimeException(ex);
        } finally {
            if (broken) {
                pool.returnBrokenResource(jedis);
            } else {
                pool.returnResource(jedis);
            }
        }
    }

    @Override
    public Long zrank(byte[] key, byte[] member) {
        ShardedRedisPool pool = getPool(key);
        Jedis jedis = pool.getResource();
        boolean broken = false;
        try {
            return jedis.zrank(key, member);
        } catch (Exception ex) {
            broken = true;
            print("zrank", pool.getShardName());
            throw new RuntimeException(ex);
        } finally {
            if (broken) {
                pool.returnBrokenResource(jedis);
            } else {
                pool.returnResource(jedis);
            }
        }
    }

    @Override
    public Long zrem(byte[] key, byte[] member) {
        ShardedRedisPool pool = getPool(key);
        Jedis jedis = pool.getResource();
        boolean broken = false;
        try {
            return jedis.zrem(key, member);
        } catch (Exception ex) {
            broken = true;
            print("zrem", pool.getShardName());
            throw new RuntimeException(ex);
        } finally {
            if (broken) {
                pool.returnBrokenResource(jedis);
            } else {
                pool.returnResource(jedis);
            }
        }
    }

    @Override
    public Long zremrangeByRank(byte[] key, int start, int end) {
        ShardedRedisPool pool = getPool(key);
        Jedis jedis = pool.getResource();
        boolean broken = false;
        try {
            return jedis.zremrangeByRank(key, start, end);
        } catch (Exception ex) {
            broken = true;
            print("zremrangeByRank", pool.getShardName());
            throw new RuntimeException(ex);
        } finally {
            if (broken) {
                pool.returnBrokenResource(jedis);
            } else {
                pool.returnResource(jedis);
            }
        }
    }

    @Override
    public Long zremrangeByScore(byte[] key, double start, double end) {
        ShardedRedisPool pool = getPool(key);
        Jedis jedis = pool.getResource();
        boolean broken = false;
        try {
            return jedis.zremrangeByScore(key, start, end);
        } catch (Exception ex) {
            broken = true;
            print("zremrangeByScore", pool.getShardName());
            throw new RuntimeException(ex);
        } finally {
            if (broken) {
                pool.returnBrokenResource(jedis);
            } else {
                pool.returnResource(jedis);
            }
        }
    }

    @Override
    public Set<byte[]> zrevrange(byte[] key, int start, int end) {
        ShardedRedisPool pool = getPool(key);
        Jedis jedis = pool.getResource();
        boolean broken = false;
        try {
            return jedis.zrevrange(key, start, end);
        } catch (Exception ex) {
            broken = true;
            print("zrevrange", pool.getShardName());
            throw new RuntimeException(ex);
        } finally {
            if (broken) {
                pool.returnBrokenResource(jedis);
            } else {
                pool.returnResource(jedis);
            }
        }
    }

    @Override
    public Set<Tuple> zrevrangeWithScores(byte[] key, int start, int end) {
        ShardedRedisPool pool = getPool(key);
        Jedis jedis = pool.getResource();
        boolean broken = false;
        try {
            return jedis.zrevrangeWithScores(key, start, end);
        } catch (Exception ex) {
            broken = true;
            print("zrevrangeWithScores", pool.getShardName());
            throw new RuntimeException(ex);
        } finally {
            if (broken) {
                pool.returnBrokenResource(jedis);
            } else {
                pool.returnResource(jedis);
            }
        }
    }

    @Override
    public Long zrevrank(byte[] key, byte[] member) {
        ShardedRedisPool pool = getPool(key);
        Jedis jedis = pool.getResource();
        boolean broken = false;
        try {
            return jedis.zrevrank(key, member);
        } catch (Exception ex) {
            broken = true;
            print("zrevrank", pool.getShardName());
            throw new RuntimeException(ex);
        } finally {
            if (broken) {
                pool.returnBrokenResource(jedis);
            } else {
                pool.returnResource(jedis);
            }
        }
    }

    @Override
    public Double zscore(byte[] key, byte[] member) {
        ShardedRedisPool pool = getPool(key);
        Jedis jedis = pool.getResource();
        boolean broken = false;
        try {
            return jedis.zscore(key, member);
        } catch (Exception ex) {
            broken = true;
            print("zscore", pool.getShardName());
            throw new RuntimeException(ex);
        } finally {
            if (broken) {
                pool.returnBrokenResource(jedis);
            } else {
                pool.returnResource(jedis);
            }
        }
    }

    @Override
    public Set<byte[]> srandomMembers(byte[] key, int count) {
        long len = this.scard(key);
        if (len == 0) {
            return null;
        }

        if (count >= len) {
            return this.smembers(key);
        }

        if (count / (double) len > 0.7) {
            return randomNeedAllMembers(key, count);
        }

        return randomMembers(key, count);
    }

    private Set<byte[]> randomMembers(byte[] key, int count) {
        Set<byte[]> members = new HashSet<byte[]>(count * 4 / 3);
        for (int i = 0; i < count;) {
            byte[] member = this.srandmember(key);
            if (!contains(members, member) && members.add(member)) {
                i++;
            }
        }
        return members;
    }

    private boolean contains(Collection<byte[]> members, byte[] member) {
        for (byte[] m : members) {
            if (Arrays.equals(m, member)) {
                return true;
            }
        }
        return false;
    }

    private Set<byte[]> randomNeedAllMembers(byte[] key, int count) {
        Set<byte[]> members = this.smembers(key);
        Set<byte[]> res = new HashSet<byte[]>();
        for (byte[] bs : members) {
            res.add(bs);
            if (res.size() >= count) {
                return res;
            }
        }
        return res;
    }

    @Override
    public Set<byte[]> sinter(byte[]... keys) {
        ShardedRedisPool pool = getPool(keys[0]);
        Jedis jedis = pool.getResource();
        try {
            return jedis.sinter(keys);
        } finally {
            pool.returnResource(jedis);
        }
    }

    @Override
    public Transaction multi(byte[] key) {
        ShardedRedisPool pool = getPool(key);
        Jedis jedis = pool.getResource();
        boolean broken = false;
        try {
            return jedis.multi();
        } catch (Exception ex) {
            broken = true;
            print("lset", pool.getShardName());
            throw new RuntimeException(ex);
        } finally {
            if (broken) {
                pool.returnBrokenResource(jedis);
            } else {
                pool.returnResource(jedis);
            }
        }
    }

    @Override
    public boolean unwatch(byte[] key) {
        ShardedRedisPool pool = getPool(key);
        Jedis jedis = pool.getResource();
        boolean broken = false;
        try {
            return convertStateReply(jedis.unwatch());
        } catch (Exception ex) {
            broken = true;
            print("lset", pool.getShardName());
            throw new RuntimeException(ex);
        } finally {
            if (broken) {
                pool.returnBrokenResource(jedis);
            } else {
                pool.returnResource(jedis);
            }
        }
    }

    @Override
    public boolean watch(byte[] key) {
        ShardedRedisPool pool = getPool(key);
        Jedis jedis = pool.getResource();
        boolean broken = false;
        try {
            return convertStateReply(jedis.watch(key));
        } catch (Exception ex) {
            broken = true;
            print("lset", pool.getShardName());
            throw new RuntimeException(ex);
        } finally {
            if (broken) {
                pool.returnBrokenResource(jedis);
            } else {
                pool.returnResource(jedis);
            }
        }
    }

	@Override
	public ShardedRedisPool getShardedRedisPool(byte[] key) {
		ShardedRedisPool pool = getPool(key);
		return pool;
	}

	@Override
	public Object eval(byte[] script, int keyCount, byte[]... params) {
		ShardedRedisPool pool = getPool(params[0]);
		Jedis jedis = pool.getResource();
		boolean broken = false;
		try {
			return jedis.eval(script, keyCount, params);
		} catch (Exception ex) {
			broken = true;
			print("eval", pool.getShardName());
			throw new RuntimeException(ex);
		} finally {
			if (broken) {
				pool.returnBrokenResource(jedis);
			} else {
				pool.returnResource(jedis);
			}
		}
	}

}
